﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics;

namespace SimpleIR
{
	/// <summary>
	/// This class is used to represent all bytecode operations load a value onto the eval stack in a basic way (from locals, args, constants, etc.).
	/// </summary>
	public class IRLoadToEvalStackBasic : IRByteCode
	{
		/// <summary>
		/// An array containing all the acceptable opcodes for this operation class (in order)
		/// </summary>
		private static OpCode.Op[] AcceptableOps = { OpCode.Op.LoadFromLocal, OpCode.Op.LoadFromGlobal, OpCode.Op.PushInt, OpCode.Op.PushFloat, OpCode.Op.PushNull, OpCode.Op.PushString, OpCode.Op.LoadFuncPtr, OpCode.Op.LoadVFuncPtr };

		/// <summary>
		/// If this instruction is a load var operation.
		/// </summary>
		private readonly IRVarName m_vararg;

		/// <summary>
		/// If this instruction is a loadconst operation.
		/// </summary>
		private readonly IRLiteralExp m_litarg;

		/// <summary>
		/// If this instruction is a load function operation this is the function that is specified.
		/// </summary>
		private readonly IRInvokeIdentity m_inv;

		/// <summary>
		/// If this instruction is a load virtual function operation this is the function sig that is specified.
		/// </summary>
		private readonly IRInvokeSig m_sig;

		/// <summary>
		/// If this instruction is a load global then this contains the static field of interest.
		/// </summary>
		private readonly IRStaticField m_staticfldarg;

		private IRLoadToEvalStackBasic(OpCode.Op opc, IRVarName vararg, IRLiteralExp litarg, IRInvokeIdentity ftn, IRInvokeSig sig, IRStaticField sfld, IRSourceLocation sloc)
			: base(opc, sloc)
		{
			Debug.Assert(AcceptableOps.Contains(opc));
			
			this.m_vararg = vararg;
			this.m_litarg = litarg;
			this.m_inv = ftn;
			this.m_sig = sig;
			this.m_staticfldarg = sfld;
		}

		public static IRLoadToEvalStackBasic MakeVarLoadOperation(IRVarName vn, IRSourceLocation sloc)
		{ return new IRLoadToEvalStackBasic(OpCode.Op.LoadFromLocal, vn, null, null, null, null, sloc); }

		public static IRLoadToEvalStackBasic MakeLoadConstOperation(IRLiteralExp lit, IRSourceLocation sloc)
		{
			IRPrimitiveType pt = (IRPrimitiveType)lit.LiteralType;
			if(pt is IRSignedIntegerType)
				return new IRLoadToEvalStackBasic(OpCode.Op.PushInt, null, lit, null, null, null, sloc);
			else
				return new IRLoadToEvalStackBasic(OpCode.Op.PushFloat, null, lit, null, null, null, sloc);
		}

		public static IRLoadToEvalStackBasic MakeLoadNullOperation(IRPtrType nullptrtype, IRSourceLocation sloc)
		{ return new IRLoadToEvalStackBasic(OpCode.Op.PushNull, null, IRLiteralExp.MakeNullLiteralExp(nullptrtype), null, null, null, sloc); }

		public static IRLoadToEvalStackBasic MakeLoadStringOperation(String s, IRPtrType stringptrtype, IRSourceLocation sloc)
		{ return new IRLoadToEvalStackBasic(OpCode.Op.PushString, null, IRLiteralExp.MakeStringLiteralExp(stringptrtype, s), null, null, null, sloc); }

		public static IRLoadToEvalStackBasic MakeStaticFieldLoadOperation(IRStaticField sf, IRSourceLocation sloc)
		{ return new IRLoadToEvalStackBasic(OpCode.Op.LoadFromGlobal, null, null, null, null, sf, sloc); }

		public static IRLoadToEvalStackBasic MakeFunctionLoadOperation(IRInvokeIdentity inv, IRSourceLocation sloc)
		{ return new IRLoadToEvalStackBasic(OpCode.Op.LoadFuncPtr, null, null, inv, null, null, sloc); }

		public static IRLoadToEvalStackBasic MakeVirtFunctionLoadOperation(IRInvokeSig sig, IRSourceLocation sloc)
		{ return new IRLoadToEvalStackBasic(OpCode.Op.LoadVFuncPtr, null, null, null, sig, null, sloc); }

		public override String ToString()
		{
			if(this.OperationCode == OpCode.Op.LoadFromLocal)
				return "ldvar:   " + this.m_vararg.ToString();
			else if(this.OperationCode == OpCode.Op.PushInt)
				return "ldci:    " + this.m_litarg.ToString();
			else if(this.OperationCode == OpCode.Op.PushFloat)
				return "ldcf:    " + this.m_litarg.ToString();
			else if(this.OperationCode == OpCode.Op.PushNull)
				return "ldnull:  ";
			else if(this.OperationCode == OpCode.Op.PushString)
				return "ldstr:   " + this.m_litarg.ToString();
			else if(this.OperationCode == OpCode.Op.LoadFromGlobal)
				return "ldsfld:  " + this.m_staticfldarg.ToString();
			else if(this.OperationCode == OpCode.Op.LoadFuncPtr)
				return "ldftn:   " + this.m_inv.ToString();
			else if(this.OperationCode == OpCode.Op.LoadVFuncPtr)
				return "ldvftn:  " + this.m_sig.ToString();
			else
				return "--BAD OPCODE--";
		}

		/// <summary>
		/// If this is a variable argument load operation (var or address) get the name of the variable argument.
		/// </summary>
		public IRVarName LocalVar
		{
			get
			{
				Debug.Assert(this.OperationCode == OpCode.Op.LoadFromLocal);
				return this.m_vararg;
			}
		}

		/// <summary>
		/// If this is a constant load (ldc, ldstr, ldnull) operation get the literal value.
		/// </summary>
		public IRLiteralExp Literal
		{
			get
			{
				Debug.Assert((this.OperationCode == OpCode.Op.PushInt) | (this.OperationCode == OpCode.Op.PushFloat) | (this.OperationCode == OpCode.Op.PushNull) | (this.OperationCode == OpCode.Op.PushString));
				return this.m_litarg;
			}
		}

		/// <summary>
		/// If this is a func load operation get the invocation identity value.
		/// </summary>
		public IRInvokeIdentity InvokeIdentity
		{
			get
			{
				Debug.Assert(this.OperationCode == OpCode.Op.LoadFuncPtr);
				return this.m_inv;
			}
		}

		/// <summary>
		/// If this is a vfunc load operation get the invocation identity value.
		/// </summary>
		public IRInvokeSig InvokeSig
		{
			get
			{
				Debug.Assert(this.OperationCode == OpCode.Op.LoadVFuncPtr);
				return this.m_sig;
			}
		}

		/// <summary>
		/// If this is a load from global operation get the static field name.
		/// </summary>
		public IRStaticField StaticField
		{
			get
			{
				Debug.Assert(this.OperationCode == OpCode.Op.LoadFromGlobal);
				return this.m_staticfldarg;
			}
		}
	}
}
